package org.wattdepot.simulation.dorm.floor;

import java.util.Random;
import javax.xml.datatype.XMLGregorianCalendar;
import org.wattdepot.client.BadXmlException;
import org.wattdepot.client.MiscClientException;
import org.wattdepot.client.NotAuthorizedException;
import org.wattdepot.client.ResourceNotFoundException;
import org.wattdepot.client.WattDepotClient;
import org.wattdepot.resource.property.jaxb.Property;
import org.wattdepot.resource.sensordata.jaxb.SensorData;
import org.wattdepot.resource.source.jaxb.Source;
import org.wattdepot.util.tstamp.Tstamp;

/**
 * Implements the set of floor loads (WattDepot sources) used in the UH Dorm simulation. The
 * WattDepot sources represent the power and energy for a pair of floors in a Hale Aloha dormitory.
 * 
 * Floor loads are generated in the following way. First, we generate the energy used during
 * the preceding 10 seconds.  (This assumes that the hypothetical sensor is polling the meter every 
 * 10 seconds.)  We assume that a room is using between 0 and 1000W of power during
 * any 10 second interval.  We randomly pick a value between 0 and 1000, then divide by 360 to get
 * the watt-hours of energy used during that 10 seconds. We do this 24 times and 
 * accumulate the results to get the overall energy used by the 24 rooms on the two floors.
 * 
 * Next, we determine if the time is during the day or night. If during the night (between 8pm and 
 * 8am) we multiply the energy by 0.60 to model the reduced energy needs during the night.
 * 
 * Finally, we are going to assume that some floors use more energy on average than others. Each 
 * floor source is initialized with a multiplier called "greenness" that varies between 1.0 and 
 * 2.0.  The result of the energy calculation so far is multiplied by the greenness factor to get 
 * the floors' actual energy consumed.  The lower the greenness factor, the more green the 
 * pair of floors.
 *   
 * @author Philip Johnson
 *
 */
public enum FloorLoad {
  /** Ilima 3,4. */
  SIM_UH_ILIMA_FLOORS_3_and_4  (1.0),
  /** Ilima 5,6. */
  SIM_UH_ILIMA_FLOORS_5_and_6  (1.0),
  /** Ilima 7,8. */
  SIM_UH_ILIMA_FLOORS_7_and_8  (1.5),
  /** Ilima 9,10. */
  SIM_UH_ILIMA_FLOORS_9_and_10  (1.0),
  /** Ilima 11,12. */
  SIM_UH_ILIMA_FLOORS_11_and_12  (1.0),
  /** Lehua 3,4. */
  SIM_UH_LEHUA_FLOORS_3_and_4  (1.5),
  /** Lehua 5,6. */
  SIM_UH_LEHUA_FLOORS_5_and_6  (1.0),
  /** Lehua 7,8. */
  SIM_UH_LEHUA_FLOORS_7_and_8  (1.0),
  /** Lehua 9,10. */
  SIM_UH_LEHUA_FLOORS_9_and_10  (1.9),
  /** Lehua 11,12. */
  SIM_UH_LEHUA_FLOORS_11_and_12  (1.0),
  /** Mokihana 3,4. */
  SIM_UH_MOKIHANA_FLOORS_3_and_4  (1.0),
  /** Mokihana 5,6. */
  SIM_UH_MOKIHANA_FLOORS_5_and_6  (1.0),
  /** Mokihana 7,8. */
  SIM_UH_MOKIHANA_FLOORS_7_and_8  (1.4),
  /** Mokihana 9,10. */
  SIM_UH_MOKIHANA_FLOORS_9_and_10  (1.0),
  /** Mokihana 11,12. */
  SIM_UH_MOKIHANA_FLOORS_11_and_12  (2.0);
    
  
  /** Stores the meters internal counter. */
  private double energyConsumedToDate = 0;

  /** The typical kWh per hour consumed during night time hours. */
  private double greenness = 0;

  /** A random number generator. */
  private final Random random = new Random(0);

  /** 
   * Specifies the instantaneous power consumed by the building at some point during the 
   * last 10 seconds. 
   */
  private double currentPower = 0;
  
  /** The name of the WattDepot source corresponding to this floor. */
  private String sourceName;
  

  /**
   * Create and initialize this building's source for energy consumed.
   * @param greenness A multiplier indicating the level of green values for this pair of floors.
   */
  private FloorLoad(double greenness) {
    this.greenness = greenness;
    this.sourceName = name().replaceAll("_and_", "-");
  }
  
  /**
   * Returns the adjustment factor for the energy consumed by this Floor given the time of day.
   * After 8pm, and before 8am, energy used is 60% of the normal daytime consumption.
   * @param interval Represents a 10 second interval through a number between 0 and 8640.
   * @return The adjusted watt-hours based upon time of day.  
   */
  private double getTimeOfDayAdjustment(int interval) {
    int eightAM = 6 * 60 * 8;
    int eightPM = 6 * 60 * 20;
    // If before 8am or after 8pm, adjust by 0.60, otherwise don't adjust.
    return ((interval < eightAM) || (interval > eightPM)) ? 0.60 : 1.0;
  }
  
  /**
   * Determines the power and energy for this floor during the past 10 seconds.
   * Assumes this is called once every 10 seconds, right before the call to sendData().
   * Power in watts is determined as a random number between 0 and 1000, multiplied by the greenness
   * factor, then adjusted by time of day.  Energy is the power divided by 360.
   * The results are stored in the instance variables. 
   * @param interval The interval corresponding to this 10 seconds. 
   */
  public void update(int interval) {
    this.currentPower = random.nextInt(1000) * this.greenness * getTimeOfDayAdjustment(interval);
    this.energyConsumedToDate += this.currentPower / 360;
  }
  

  /**
   * Determine the energy consumed for this client.
   * @param client The WattDepotClient.
   */
  public void initializeEnergyConsumed(WattDepotClient client) {
    // First, validate the source. 
    try {
      client.getSource(this.sourceName);
    }
    catch (NotAuthorizedException e) {
      System.out.format("You do not have access to the source %s. Aborting.%n", sourceName);
      throw new RuntimeException(e);
    }
    catch (ResourceNotFoundException e) {
      System.err.format("Source %s does not exist on server. Aborting.%n", sourceName);
      throw new RuntimeException(e);
    }
    catch (BadXmlException e) {
      System.err.println("Received bad XML from server, which is weird. Aborting.");
      throw new RuntimeException(e);
    }
    catch (MiscClientException e) {
      System.err.println("Had problems retrieving source from server, which is weird. Aborting.");
      throw new RuntimeException(e);
    }
    // Now, initialize its energy generated value. 
    try {
      this.energyConsumedToDate = client.getLatestEnergyConsumedToDate(sourceName);
    }
    catch (ResourceNotFoundException e) {
      System.out.format("No prior energy consumption found for source %s%n", sourceName);
    }
    catch (Exception e) {
      System.err.format("Had problems retrieving energy to date for source %s.%n", sourceName);
      throw new RuntimeException(e);
    }
    System.out.format("%25s energy consumed to date:  %12.2f kWh%n", sourceName, 
        (this.energyConsumedToDate / 1000));
  }

  /**
   * Sends data about the current energy consumed and energy consumed to date to WattDepot.
   * Assumes this is called approximately once every 10 seconds. 
   * @param client The WattDepotClient.
   */
  public void sendData(WattDepotClient client) {
    XMLGregorianCalendar timestamp = Tstamp.makeTimestamp();
    String toolName = "UHDormSimulation";
    String sourceURI = Source.sourceToUri(sourceName, client.getWattDepotUri());
    SensorData data = new SensorData(timestamp, toolName, sourceURI);
    data.addProperty(new Property(SensorData.POWER_CONSUMED, currentPower));
    data.addProperty(new Property(SensorData.ENERGY_CONSUMED_TO_DATE, energyConsumedToDate));
    try {
      client.storeSensorData(data);
      System.out.format("%s %35s %10.2f kW %6.2f kWh%n", timestamp, sourceName, 
          (currentPower / 1000), (energyConsumedToDate / 1000));

    }
    catch (Exception e) {
      System.out.println("Error while sending data to WattDepot: " + e.getMessage());
    }
  }

}
